날짜, 시간 다루기
1. Java의 Date, LocalDate, Calendar
자바나 코틀린에서 날짜, 요일과 관련된 조작을 하기 위해 검색을 하다보면 일본의 돈키호테 안에 들어선 기분이 들 수 밖에 없을 것이다. 어떤 포스트에서는 Date와 Calendar로 방법을 알려주고 다른 포스트에서는 LocalDate로 방법을 알려준다. 게다가 formatter, parse 메서드까지 얹어주면 난 정말 모르겠어... 가 된다. 이제는 정리하고 가자.
1-1. Date, Calendar 쓰지 마세요.
java.util.Date
java.util.Calendar
java.text.DateFormat
java.text.DateFormat > java.text.SimpleDateFormat
쓰지 말라고 한다.
Date와 Calendar을 사용하지 말라고 하는 이유는 석이님의 블로그에 잘 나와있기 때문에 이것을 참고하도록 하자. (날짜와 시간을 0부터 센다는것 부터 숙연해진다...)
이를 개선하기위해 Java는 Java8에서 LocalDate를 제공하게 된다. 참고로 Java8은 2014년에 첫 릴리즈가 되었다. 아직까지도 따라다니는 Date와 Calendar의 그림자는 무엇이란말인가...
1-2. 이젠 LocalDate를 씁시다.
java.time.LocalDateTime
java.time.LocalTime
java.time.LocalDate
java.time.DateTimeFormatter
자, 이제 위의 것만 기억하면 된다.
LocalTime은 시, 분, 초에 관한 것, LocalDate는 년,월,일에 관한 것, 그리고 LocalDateTime은 년,월,일,시,분,초에 관한것이다.
보통 이 클래스들은 특정일로부터 N일, N개월 전 후의 날짜나 날짜들의 선후관계 비교등을 위해 사용한다. 클래스들의 메소드들을 보면서 사용방법을 알기 전에 Formatter에 대해서 먼저 알아보자
1-3. DateTimeFormatter
말 그대로 형식 양식이다. 2022년 3월 1일을 여러 형식을 사용해서 표현하면 22.3.1, 22.03.01, 22-3-1, 22-03-01, 2022-3-1등등 수만가지로 표현이 가능하다. 이것 중에 어떤 것을 사용할 것인지 (어떤 형식으로 출력할 것인지, 어떤 형식으로 저장할 것인지)를 정할 수 있게 만든 객체이다.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy MM dd");
이런 형태로 사용한다.
String to LocalDate
여기서 주의해야할 점은
- 변경되려고 하는 문자열이 formatter의 형식에 맞아야 한다.
- parse메서드는 LocalDate의 static 메서드이다.
String dateStr = "2022 03 01" //2022-03-01이면 DateTimeParseException, 2022 03 01 AM 9이여도 DateTimeParseException
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy MM dd")
LocalDate localDate = LocalDate.parse(dateStr, formatter)
LocalDate to String
- LocalDate를 초기화 하는 방식은 of, now등의 static 메서드를 사용
- string으로 변경하는 format 메서드는 LocalDate의 인스턴스 메서드
LocalDate localDate = LocalDate.of(2022, Month.March, 1)
//LocalDate localDate = LocalDate.now()
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy MM dd")
String localDateStr = localDate.format(formatter) //2022 03 01
1-4. 자 이제 Localxxx들의 자주 사용하는 메서드를 보자
위에서 봤던 parse나 format, of나 now를 제외하고 본다면,
비교
isAfter(), isBefore(), isEqual()
조회
getHour(), getMonth(), ...
더하고 빼기
plusDays(), minusMonths(), plusHours(), minusSeconds(), ...
새 객체 생성 (Localxxx는 immutable한 객체이다.)
withMonth(), withSecond(), ...
특정 날짜의 월의 마지막 날짜 조회
LocalDate localDate = LocalDate.of(2022, Month.March, 1)
LocalDate lastDateOfLocalDate = localDate.withDayOfMonth(localDate.lengthOfMonth())
두 객체 사이의 차이 (날짜 간격, 시간 간격)
- 시간간격 Duration
LocalDate localDate1 = LocalDate.of(2022, Month.March, 1)
LocalDate localDate2 = LocalDate.of(2022, Month.March, 4)
Long hourGap = Duration.between(localDate1, localDate2).toHour()
//between의 파라미터인 Temporal의 자식 클래스가 Localxxx이다.
- 날짜 간격 Period
LocalDate localDate1 = LocalDate.of(2022, Month.March, 1)
LocalDate localDate2 = LocalDate.of(2022, Month.March, 4)
int dateGap = Period.between(localDate1, localDate2).getDays()
2. Kotlin의 그것
위의 Localxxx는 코틀린도 동일하게 사용할 수 있습니다.
여기에서는 Localxxx 함수를 보는것은 자바와 같은 사용방법이기 때문에 의미가 없고, Formatter를 뜯어보는 것이 의미가 있을 것 같다.
2-1. Kotlin의 Formatter, DateTimeFormat!
datetime의 format 메서드를 보면, formatter 파라미터의 타입이 DateTimeFormat<DateTimeComponents>
이다. 여기서 DateTimeFormat은 format이나 parsing을 위한 기능을 명세한 인터페이스이고 이것을 이용하여 Builder 인터페이스를 만들고 Kotlin DSL을 생성한다. DateTimeComponents는 date, time 형식 값을 나타내며 역시 Kotlin DSL을 생성하는데 사용된다. DateTimeFormat 객체는 두가지 방식으로 생성이 가능한데, 하나는 Unicode Pattern 방식이고 다른 하나는 KotlinDSL 방식이다. (참고: Date and Time Formatting in Kotlin with the DateTime Library, 노루룽, 행뽁)
Unicode Pattern 방식
// 2024 07 17 - 06:51
currentDateTime.format(LocalDateTime.Format { byUnicodePattern("yyyy MM dd - HH:mm") })
위의 방식처럼 우리가 쓰던 패턴 형식을 가지고 formatter를 만드는 것을 말한다.
KotlinDSL 방식
// Jul 17, 2024
currentDate.format(LocalDate.Format {
monthName(MonthNames.ENGLISH_ABBREVIATED)
char(' ')
dayOfMonth()
chars(", ")
year()
})
KotlinDSL을 이용하여 formatter를 만드는 것을 말한다.